﻿using ClockInLearning.Db;
using ClockInLearning.Dto;
using ClockInLearning.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ClockInLearning.Impl
{
    public class UserInfoApplication
    {
        private readonly AppDbContext _context;

        public UserInfoApplication(AppDbContext context)
        {
            _context = context;
        }

        public async Task<UserInfoOutputDto> Test()
        {
            //var rtnVal = _context.UserInfos.FirstOrDefault();
            return null;
        }

        public async Task<ServiceResult> CheckToken(string token)
        {
            var result = new ServiceResult();

            var tokenData = _context.Tokens.Where(x => x.Token == token).FirstOrDefault();
            if (tokenData == null)
            {
                result.IsFailed("token验证失败");
            }
            else if ((DateTime.Now - tokenData.LastTime).TotalMinutes > 60)
            {
                result.IsFailed("token超时");
            }
            else
            {
                tokenData.LastTime = DateTime.Now;
                await _context.SaveChangesAsync();
                result.IsSuccess();
            }

            return result;
        }

        public async Task<ServiceResult<UserInfoLoginDto>> Login(LoginDto dto)
        {
            var result = new ServiceResult<UserInfoLoginDto>();

            // 加密
            dto.Password = PwdEncryption(dto.Account, dto.Password);

            var thisUser = _context.UserInfos.Where(x => dto.Account == x.Account && dto.Password == x.Password).FirstOrDefault();
            //var thisUser = _context.UserInfos.Where(x => (dto.Account == x.Account || dto.Account == x.Phone || dto.Account == x.Email)
            //     && dto.Password == x.Password).FirstOrDefault();

            if (thisUser == null)
            {
                result.IsFailed("登录名或密码错误。");
                return result;
            }

            // 查看是否已有对应有效token，若没有则创建
            var thisToken = _context.Tokens.Where(x => x.UserId == thisUser.Id && DateTime.Now.AddMinutes(-60) <= x.LastTime).FirstOrDefault();
            if (thisToken == null)
            {
                thisToken = new TokenInfo()
                {
                    Id = Guid.NewGuid(),
                    UserId = thisUser.Id,
                    Ip = string.Empty,
                    Token = Encryption.MD5Encryption(Guid.NewGuid().ToString() + "kas233" + DateTime.Now.ToShortTimeString()),
                    LastTime = DateTime.Now
                };
                await _context.Tokens.AddAsync(thisToken);
                await _context.SaveChangesAsync();
            }
            else
            {
                thisToken.LastTime = DateTime.Now;
                _context.Tokens.Update(thisToken);
            }

            var rtnObject = thisUser.SimpleMapTo<UserInfoAndWeekStaticDto>();
            GetUserInfoAndWeekStatic(rtnObject, thisUser.Id);
            result.IsSuccess(new UserInfoLoginDto()
            {
                Token = thisToken.Token,
                UserInfo = rtnObject
            });
            return result;
        }

        public async Task<ServiceResult> Logout()
        {
            var result = new ServiceResult();

            var tokenInfo = CallContext.GetData("token") as TokenInfo;
            tokenInfo.LastTime = DateTime.Parse("1900-01-01");
            await _context.SaveChangesAsync();

            result.IsSuccess();

            return result;
        }

        public async Task<ServiceResult<UserInfoOutputDto>> GetUserInfo()
        {
            var result = new ServiceResult<UserInfoOutputDto>();
            var userInfo = _context.GetUserInfoByToken<UserInfoOutputDto>();
            result.IsSuccess(userInfo);
            return result;
        }

        public async Task<ServiceResult<UserInfoAndWeekStaticDto>> GetUserInfoAndWeekStatic()
        {
            var result = new ServiceResult<UserInfoAndWeekStaticDto>();
            var userInfo = _context.GetUserInfoByToken<UserInfo>();

            var rtnObject = userInfo.SimpleMapTo<UserInfoAndWeekStaticDto>();
            GetUserInfoAndWeekStatic(rtnObject, userInfo.Id);

            result.IsSuccess(rtnObject);
            return result;
        }
        private void GetUserInfoAndWeekStatic(UserInfoAndWeekStaticDto dto, Guid? userId = null)
        {
            var weekStart = GetWeekStartDate();
            dto.StaticTitle = $"{weekStart:yyyy-MM-dd} ~ {weekStart.AddDays(6):yyyy-MM-dd}";
            dto.AddRegistStatic = new PointsRegistApplication(_context).GetWeekRegistStatics(OperationTypeEnum.Add, weekStart, userId);
            dto.UseRegistStatic = new PointsRegistApplication(_context).GetWeekRegistStatics(OperationTypeEnum.Use, weekStart, userId);
        }

        public async Task<ServiceResult<UserInfoLoginDto>> Regist(RegistDto dto)
        {
            var result = new ServiceResult<UserInfoLoginDto>();

            var repeatCheckFlag = _context.UserInfos.Where(x => x.Account == dto.Account).Any();
            if (repeatCheckFlag)
            {
                result.IsFailed("输入账号已存在");
                return result;
            }

            // 加密
            dto.Password = PwdEncryption(dto.Account, dto.Password);

            var newUser = new UserInfo()
            {
                Id = Guid.NewGuid(),
                Account = dto.Account,
                UserName = "user" + DateTime.Now.ToString("mmsshh"),
                Password = dto.Password,
                Avatar = "", //默认头像
                PrivatePoints = 0,
                RecognizedPoints = 0
            };
            var thisToken = new TokenInfo()
            {
                Id = Guid.NewGuid(),
                UserId = newUser.Id,
                Ip = string.Empty,
                Token = Encryption.MD5Encryption(Guid.NewGuid().ToString() + "kas233" + DateTime.Now.ToShortTimeString()),
                LastTime = DateTime.Now
            };
            await _context.AddAsync(newUser);
            await _context.AddAsync(thisToken);
            await _context.SaveChangesAsync();


            var rtnObject = newUser.SimpleMapTo<UserInfoAndWeekStaticDto>();
            rtnObject.AddRegistStatic = new List<int> { 0, 0, 0, 0, 0, 0, 0 };
            rtnObject.UseRegistStatic = new List<int> { 0, 0, 0, 0, 0, 0, 0 };
            result.IsSuccess(new UserInfoLoginDto()
            {
                Token = thisToken.Token,
                UserInfo = rtnObject
            });
            return result;
        }

        public async Task<ServiceResult> Update(UserInfoInputDto dto)
        {
            var result = new ServiceResult();
            var hisUserInfo = _context.GetUserInfoByToken<UserInfo>();
            
            // 账号不允许修改
            if (hisUserInfo.Account != dto.Account)
            {
                result.IsFailed("账号不允许修改");
                return result;
            }

            hisUserInfo.Avatar = dto.Avatar;
            hisUserInfo.UserName = dto.UserName;
            hisUserInfo.Phone = dto.Phone;
            hisUserInfo.Email = dto.Email;

            // 更新
            _context.UserInfos.Update(hisUserInfo.SimpleMapTo<UserInfo>());
            await _context.SaveChangesAsync();

            result.IsSuccess();
            return result;
        }

        public async Task<ServiceResult> ModifyPwd(ModifyPwdDto dto)
        {
            var result = new ServiceResult();
            var userInfo = _context.GetUserInfoByToken<UserInfo>();
            if (PwdEncryption(userInfo.Account, dto.OldPassword) != userInfo.Password)
            {
                result.IsFailed("旧密码不正确");
                return result;
            }
            if (PwdEncryption(userInfo.Account, dto.NewPassword) == userInfo.Password)
            {
                result.IsFailed("新密码与原密码相同");
                return result;
            }
            userInfo.Password = PwdEncryption(userInfo.Account, dto.NewPassword);
            _context.UserInfos.Update(userInfo);
            await _context.SaveChangesAsync();

            result.IsSuccess();
            return result;
        }

        public async Task<ServiceResult<List<UserListDto>>> QueryRelationUsers()
        {
            var result = new ServiceResult<List<UserListDto>>();

            var userInfo = _context.GetUserInfoByToken<UserInfoOutputDto>();

            var allRelations = _context.UserRelations.Where(x => x.UserId == userInfo.Id && x.IsDelete == false)?.ToList();

            if (allRelations == null || !allRelations.Any())
            {
                result.IsSuccess(new List<UserListDto>());
            }
            else
            {
                var allRelationUsers = _context.UserInfos
                    .Where(x => allRelations.Select(y => y.SupervisorId).Contains(x.Id))
                    ?.Select(x => x.SimpleMapTo<UserListDto>()).ToList();

                if (allRelationUsers == null || !allRelationUsers.Any())
                {
                    result.IsSuccess(new List<UserListDto>());
                }
                else
                {
                    result.IsSuccess(allRelationUsers);
                }
            }

            return result;
        }

        //密码加密
        //先加上固定值，再加上用户账号，防止相同密码值相同
        private string PwdEncryption(string userAccount, string password)
        {
            password += "kas233" + userAccount;
            return Encryption.MD5Encryption(password);
        }

        private DateTime GetWeekStartDate()
        {
            var weekStart = DateTime.Now.AddDays(DateTime.Now.DayOfWeek == DayOfWeek.Sunday ? -7 : -(int)(DateTime.Now.DayOfWeek - 1));
            return weekStart.Date;
        }
    }
}
